﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Text;

namespace LeadChina.NETFramework.Common
{
    /// <summary>
    /// 读文件操作类
    /// </summary>
    public static class FileReader
    {
        private delegate void FileInvoke();

        private static readonly Encoding DefaultEncoding = Encoding.GetEncoding("GB2312");
        #region FileInfo
        /// <summary>
        /// 读取文件版本和更新日期
        /// </summary>
        /// <param name="FullName"></param>
        /// <param name="Version"></param>
        /// <param name="UpdateTime"></param>
        /// <returns></returns>
        public static bool ReadFileInfo(string FullName, ref string Version, ref string UpdateTime)
        {
            try
            {
                var file = new FileInfo(FullName);
                if (!file.Exists) return false;
                var version = FileVersionInfo.GetVersionInfo(file.FullName);
                UpdateTime = file.LastWriteTime.ToString("yyyy/MM/dd HH:mm");
                Version = version.FileVersion;
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// 遍历Folder的文件名
        /// </summary>
        /// <param name="Folder"></param>
        /// <param name="ScanChildFolder">是否遍历子文件夹</param>
        /// <returns></returns>
        public static List<string> ScanFolder(string Folder, bool ScanChildFolder = true)
        {
            var FileNames = new List<string>();
            try
            {
                FileSystemInfo theFolder = new DirectoryInfo(Folder);
                if (!((DirectoryInfo)theFolder).Exists) return null;
                Scan(ref FileNames, theFolder, ScanChildFolder);
                return FileNames;
            }
            catch
            {
                throw;
            }
            
        }
        /// <summary>
        /// 扫描
        /// </summary>
        /// <param name="FileNames"></param>
        /// <param name="theFolder"></param>
        /// <param name="ScanChildFolder">是否扫描子目录</param>
        private static void Scan(ref List<string> FileNames, FileSystemInfo theFolder, bool ScanChildFolder = true)
        {
            var Files = ((DirectoryInfo)theFolder).GetFiles();
            foreach (var file in Files)
            {
                if (file.Exists) FileNames.Add(file.FullName);//是文件
                else if (ScanChildFolder) Scan(ref FileNames, file);//是目录
            }
        }

        ///// <summary>
        ///// ListView显示路径下所有文件以及子文件夹中文件
        ///// </summary>
        ///// <param name="listView"></param>
        ///// <param name="Folder"></param>
        ///// <param name="scanChildFolder"></param>
        //public static void ShowFolderFilesInListView(ListView listView, string Folder, bool scanChildFolder = true)
        //{
        //    var theFolder = new DirectoryInfo(Folder);
        //    try
        //    {
        //        listView.BeginInvoke(new FileInvoke(() =>
        //            {
        //                listView.Items.Clear();
        //                listView.Columns.Clear();
        //                listView.Columns.Add("文件名", listView.Width / 2);
        //                listView.Columns.Add("版本号", listView.Width / 8);
        //                listView.Columns.Add("更新时间", listView.Width / 4);
        //                listView.Columns.Add("文件大小", listView.Width / 8);
        //                if (!(theFolder is DirectoryInfo MainDir)) return;
        //                var files = MainDir.GetFiles();
        //                for (var i = 0; i < files.Length; i++)
        //                {
        //                    var file = files[i];
        //                    if (file != null)
        //                    {
        //                        var info = FileVersionInfo.GetVersionInfo(file.FullName);
        //                        listView.Items.Add(i.ToString(), file.Name, 0);
        //                        listView.Items[i.ToString()].SubItems.Add(info.FileVersion);
        //                        listView.Items[i.ToString()].SubItems.Add(file.LastWriteTime.ToString("yyyy/MM/dd HH:mm"));
        //                        listView.Items[i.ToString()].SubItems.Add($"{file.Length / 1024}KB");
        //                    }
        //                    else if (scanChildFolder) ShowFolderFilesInListView(listView, files[i].FullName);
        //                }
        //                //listView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
        //            })
        //        );
        //    }
        //    catch
        //    {
        //        throw;
        //    }
        //}

        #endregion

        #region Read Files

        /// <summary>
        /// 可读
        /// 1.txt
        /// 2.ini
        /// 3.settings
        /// 4.config
        /// 5.log
        /// 6.sql
        /// 文件
        /// </summary>
        /// <param name="FullName"></param>
        /// <returns></returns>
        public static string ReadText(string FullName)
        {
            string rst = "";
            try
            {
                var file = new FileInfo(FullName);
                var ExtentionFilter = new List<string>();
                ExtentionFilter.AddRange(new string[] {".txt", ".ini", ".settings", ".config", ".log", ".sql"});
                var legal = false;
                foreach (var Extention in ExtentionFilter)
                {
                    legal = string.Equals(file.Extension, Extention, StringComparison.OrdinalIgnoreCase);
                    if (legal) break;
                }

                if (!(file.Exists && legal)) return null;
                using (var fs = new FileStream(FullName, FileMode.Open, FileAccess.Read, FileShare.Read))
                {
                    if (fs.CanRead)
                        rst = File.ReadAllText(FullName, DefaultEncoding);
                    fs.Close();
                }
                return rst;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// 将CSV文件的数据读取到DataTable中
        /// </summary>
        /// <param name="filePath">CSV文件路径</param>
        /// <returns>返回读取了CSV数据的DataTable</returns>
        public static DataTable ReadCSV(string filePath)
        {
            var encoding = GetType(filePath); 
            var dt = new DataTable();
            var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            var sr = new StreamReader(fileStream, encoding);
            //记录每次读取的一行记录
            string strLine = "";
            //记录每行记录中的各字段内容
            string[] aryLine = null;
            string[] tableHead = null;
            //标示列数
            var columnCount = 0;
            //标示是否是读取的第一行
            var IsFirst = true;
            //逐行读取CSV中的数据
            while ((strLine = sr.ReadLine()) != null)
            {
                if (IsFirst == true)
                {
                    tableHead = strLine.Split(',');
                    IsFirst = false;
                    columnCount = tableHead.Length;
                    //创建列
                    for (var i = 0; i < columnCount; i++)
                    {
                        var dc = new DataColumn(tableHead[i]);
                        dt.Columns.Add(dc);
                    }
                }
                else
                {
                    aryLine = strLine.Split(',');
                    DataRow dr = dt.NewRow();
                    for (int j = 0; j < columnCount; j++)
                    {
                        dr[j] = j > aryLine.Length-1 ? "":aryLine[j];
                    }
                    dt.Rows.Add(dr);
                }
            }
            if (aryLine != null && aryLine.Length > 0)
            {
                dt.DefaultView.Sort = tableHead[0] + " " + "asc";
            }
            sr.Close();
            fileStream.Close();
            return dt;
        }
        /// <summary>
        /// 打开CSV 文件
        /// </summary>
        /// <param name="filePath">文件全名</param>
        /// <param name="firstRow">开始行</param>
        /// <param name="firstColumn">开始列</param>
        /// <param name="getRows">获取多少行</param>
        /// <param name="getColumns">获取多少列</param>
        /// <param name="haveTitleRow">是否有标题行</param>
        /// <returns>DataTable</returns>
        public static DataTable ReadCSV(string filePath, short firstRow = 0, short firstColumn = 0, short getRows = 0, short getColumns = 0, bool haveTitleRow = true)
        {
            var dt = new DataTable();
            var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);          
            var streamReader = new StreamReader(fileStream, GetType(filePath));
            //记录每次读取的一行记录
            var stringLine = "";           
            //标示列数
            var columnCount = 0;
            //是否已建立了表的列
            var bCreateTableColumns = false;
            //第几行
            var iRow = 1;
            //去除无用行
            if (firstRow > 0)
                for (var i = 1; i < firstRow; i++)
                    streamReader.ReadLine();
            // { ",", ".", "!", "?", ";", ":", " " };
            string[] separators = { "," };
            //逐行读取CSV中的数据
            while ((stringLine = streamReader.ReadLine()) != null)
            {
                stringLine = stringLine.Trim();
                //记录每行记录中的各字段内容
                var arrayLine = stringLine.Split(separators,StringSplitOptions.RemoveEmptyEntries);
                if (!bCreateTableColumns)
                {
                    columnCount = arrayLine.Length;                   
                    for (int i = firstColumn; i < (getColumns == 0 ? columnCount : firstColumn + getColumns); i++)                    
                        dt.Columns.Add(new DataColumn(haveTitleRow ? arrayLine[i] : $"COL{i}"));   //创建列                 
                    bCreateTableColumns = true;
                    if (haveTitleRow) continue;                    
                }
                var dr = dt.NewRow();
                for (int j = firstColumn; j < (getColumns == 0 ? columnCount : firstColumn + getColumns); j++)
                    dr[j - firstColumn] = arrayLine[j];
                dt.Rows.Add(dr);
                iRow++;
                if (getRows <= 0) continue;
                if (iRow > getRows) break;
            }
            streamReader.Close();
            fileStream.Close();
            return dt;
        }
        #endregion

        #region Common

        /// 给定文件的路径，读取文件的二进制数据，判断文件的编码类型
        /// <param name="fileName">文件路径</param>
        /// <returns>文件的编码类型</returns>

        public static Encoding GetType(string fileName)
        {
            Encoding encode;
            using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
                encode = GetType(fs);
            return encode;
        }

        /// 通过给定的文件流，判断文件的编码类型
        /// <param name="fileStream">文件流</param>
        /// <returns>文件的编码类型</returns>
        public static Encoding GetType(FileStream fileStream)
        {
            var Unicode = new byte[] {0xFF, 0xFE, 0x41};
            var UnicodeBIG = new byte[] {0xFE, 0xFF, 0x00};
            var UTF8 = new byte[] {0xEF, 0xBB, 0xBF}; //带BOM
            var reVal = Encoding.Default;
            using (var reader = new BinaryReader(fileStream, Encoding.Default))
            {
                int.TryParse(fileStream.Length.ToString(), out var i);
                var ss = reader.ReadBytes(i);
                if (IsUTF8Bytes(ss) || (ss[0] == 0xEF && ss[1] == 0xBB && ss[2] == 0xBF)) reVal = Encoding.UTF8;
                else if (ss[0] == 0xFE && ss[1] == 0xFF && ss[2] == 0x00) reVal = Encoding.BigEndianUnicode;
                else if (ss[0] == 0xFF && ss[1] == 0xFE && ss[2] == 0x41) reVal = Encoding.Unicode;
                else reVal = DefaultEncoding;
            }
            return reVal;
        }

        /// 判断是否是不带 BOM 的 UTF8 格式
        /// <param name="data"></param>
        /// <returns></returns>
        private static bool IsUTF8Bytes(byte[] data)
        {
            if (data == null) throw new ArgumentNullException(nameof(data));
            var charByteCounter = 1;  //计算当前正分析的字符应还有的字节数
            foreach (var b in data)
            {
                var curByte = b; //当前分析的字节.
                if (charByteCounter == 1)
                {
                    if (curByte < 0x80) continue;
                    //判断当前
                    while (((curByte <<= 1) & 0x80) != 0)
                        charByteCounter++;
                    //标记位首位若为非0 则至少以2个1开始 如:110XXXXX...........1111110X　
                    if (charByteCounter == 1 || charByteCounter > 6)
                        return false;
                }
                else
                {
                    //若是UTF-8 此时第一位必须为1
                    if ((curByte & 0xC0) != 0x80)
                        return false;
                    charByteCounter--;
                }
            }
            if (charByteCounter > 1)
                throw new Exception("非预期的byte格式");
            return true;
        }
        #endregion
    }
}
